#pragma once
#include <glm/glm.hpp>

namespace glm
{
    template<typename T>
    struct tmat8x3 
    {
        typedef tvec3<T> col_type;

    private:
        col_type value[8];

    public:
        //-- Constructors--
        GLM_FUNC_DECL tmat8x3()
        {
            value[0] = col_type(0, 0, 0);
            value[1] = col_type(0, 0, 0);
            value[2] = col_type(0, 0, 0);
            value[3] = col_type(0, 0, 0);
            value[4] = col_type(0, 0, 0);
            value[5] = col_type(0, 0, 0);
            value[6] = col_type(0, 0, 0);
            value[7] = col_type(0, 0, 0);
        }

        GLM_FUNC_DECL explicit tmat8x3(T const & x)
        {
            value[0] = col_type(x, 0, 0);
            value[1] = col_type(0, x, 0);
            value[2] = col_type(0, 0, x);
            value[3] = col_type(0, 0, 0);
            value[4] = col_type(0, 0, 0);
            value[5] = col_type(0, 0, 0);
            value[6] = col_type(0, 0, 0);
            value[7] = col_type(0, 0, 0);
        }

        GLM_FUNC_DECL explicit tmat8x3(
            col_type const& v0,
            col_type const& v1, 
            col_type const& v2, 
            col_type const& v3, 
            col_type const& v4, 
            col_type const& v5, 
            col_type const& v6, 
            col_type const& v7 )
        {
            value[0] = v0;
            value[1] = v1;
            value[2] = v2;
            value[3] = v3;
            value[4] = v4;
            value[5] = v5;
            value[6] = v6;
            value[7] = v7;
        }

        //--Access--
        typedef length_t length_type;
        GLM_FUNC_DECL col_type & operator[](length_type i) { return value[i]; }
        GLM_FUNC_DECL col_type const & operator[](length_type i) const { return value[i]; }
    };

    // -- unary operators --

    template <typename T>
    GLM_FUNC_DECL tmat8x3<T> operator+(tmat4x3<T> const & m) { return m; }

    template <typename T>
    GLM_FUNC_DECL tmat8x3<T> operator-(tmat4x3<T> const & m)
    {
        return tmat8x3<T>(
            -m[0],
            -m[1],
            -m[2],
            -m[3],
            -m[4],
            -m[5],
            -m[6],
            -m[7]);
    }
}
    // -- binary operators --

    template <typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator+(glm::tmat8x3<T> const &m1, glm::tmat8x3<T> const &m2)
    {
        return glm::tmat8x3<T>(
            m1[0] + m2[0],
            m1[1] + m2[1],
            m1[2] + m2[2],
            m1[3] + m2[3],
            m1[4] + m2[4],
            m1[5] + m2[5],
            m1[6] + m2[6],
            m1[7] + m2[7]);
    }

    template <typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator-(glm::tmat8x3<T> const &m1, glm::tmat8x3<T> const &m2)
    {
        return glm::tmat8x3<T>(
            m1[0] - m2[0],
            m1[1] - m2[1],
            m1[2] - m2[2],
            m1[3] - m2[3],
            m1[4] - m2[4],
            m1[5] - m2[5],
            m1[6] - m2[6],
            m1[7] - m2[7]);
    }

    template <typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator*(glm::tmat8x3<T> const & m, T const & s)
    {
        return glm::tmat8x3<T>(
            m[0] * s,
            m[1] * s,
            m[2] * s,
            m[3] * s,
            m[4] * s,
            m[5] * s,
            m[6] * s,
            m[7] * s);
    }

    template <typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator*(T const & s, glm::tmat8x3<T> const & m)
    {
        return glm::tmat8x3<T>(
            m[0] * s,
            m[1] * s,
            m[2] * s,
            m[3] * s,
            m[4] * s,
            m[5] * s,
            m[6] * s,
            m[7] * s);
    }

    template <typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator/(glm::tmat8x3<T> const & m, T const & s)
    {
        return glm::tmat8x3<T>(
            m[0] / s,
            m[1] / s,
            m[2] / s,
            m[3] / s,
            m[4] / s,
            m[5] / s,
            m[6] / s,
            m[7] / s);
    }

    template <typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator/(T const & s, glm::tmat8x3<T> const & m)
    {
        return glm::tmat8x3<T>(
            s / m[0],
            s / m[1],
            s / m[2],
            s / m[3],
            s / m[4],
            s / m[5],
            s / m[6],
            s / m[7]);
    }

    template<typename T>
    GLM_FUNC_DECL glm::tmat3x3<T> operator*(glm::tmat8x3<T> const &m1, glm::tmat8x3<T> const &m2)
    {
        glm::tmat3x3<T> sum(static_cast<T>(0.0));
        for (auto i = 0; i<8; ++i)
        {
            sum += glm::outerProduct(m1[i], m2[i]);
        }
        return sum;
    }

    template<typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator*(glm::tmat8x3<T> const &m1, glm::tmat3x3<T> const &m2)
    {
        glm::tmat8x3<T> ret(static_cast<T>(0.0));
        for (auto i = 0; i<8; ++i)
        {
            ret[i] = m2 * m1[i];
        }
        return ret;
    }

    template<typename T>
    GLM_FUNC_DECL glm::tmat8x3<T> operator*(glm::tmat3x3<T> const &m1, glm::tmat8x3<T> const &m2)
    {
        glm::tmat8x3<T> ret(static_cast<T>(0.0));
        for (auto i = 0; i<8; ++i)
        {
            ret[i] = m1 * m2[i];
        }
        return ret;
    }